package com.flynn.rock.config;

import com.flynn.rock.handler.SysAccessDeniedHandler;
import com.flynn.rock.handler.SysLogoutSuccessHandler;
import com.flynn.rock.handler.SysNotLoginProcessHandler;
import com.flynn.rock.jwt.JwtAuthenticationConfigurer;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.time.Duration;
import java.util.Collections;
import java.util.List;

@Configuration
public class SysSecurityConfig {

    @Resource
    private SysNotLoginProcessHandler sysNotLoginProcessHandler;

    @Resource
    private SysAccessDeniedHandler sysAccessDeniedHandler;

    @Resource
    private SysLogoutSuccessHandler sysLogoutSuccessHandler;

    @Resource
    private SysAuthenticationConfigurer sysAuthenticationConfigurer;

    @Resource
    private JwtAuthenticationConfigurer jwtAuthenticationConfigurer;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {

        httpSecurity.securityMatcher("/**")
                .authorizeHttpRequests(request -> {
                    request
                            .requestMatchers(
                                    "/sys/login",
                                    "/sys/logout",
                                    "/swagger-ui/**",
                                    "/swagger/**",
                                    "/static/**",
                                    "/webjars/**",
                                    "**.html",
                                    "**.css",
                                    "**.js"
                            ).permitAll()
                            .anyRequest().authenticated();
                });

        httpSecurity.logout(logout -> {
            logout
                    .logoutUrl("/sys/logout")
                    .logoutSuccessHandler(sysLogoutSuccessHandler)
                    .clearAuthentication(true);
        });

        httpSecurity.exceptionHandling(exception -> {
            exception
                    .authenticationEntryPoint(sysNotLoginProcessHandler)
                    .accessDeniedHandler(sysAccessDeniedHandler);
        });

        httpSecurity.apply(sysAuthenticationConfigurer);
        httpSecurity.apply(jwtAuthenticationConfigurer);

        httpSecurity.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        httpSecurity.csrf(AbstractHttpConfigurer::disable);

        httpSecurity.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(this.corsConfigurationSource()));
        return httpSecurity.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        List<String> configPaths = Collections.singletonList("*");
        corsConfiguration.setAllowCredentials(Boolean.TRUE);
        corsConfiguration.setAllowedHeaders(configPaths);
        corsConfiguration.setAllowedMethods(configPaths);
        corsConfiguration.setAllowedOriginPatterns(configPaths);
        corsConfiguration.setMaxAge(Duration.ofDays(1));
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }
}
